#pragma once
#include<vector>
#include"Thread.hpp"
#include<memory>
#include<queue>
#include"Mutex.hpp"
#include"Cond.hpp"
#include"log.hpp"
#include<functional>
namespace ThreadPoolModule
{
    using namespace LockModule;
    using namespace CondModule;
    using namespace ThreadModule;
    using namespace LogModule;
    
    using thread_t = std::shared_ptr<Thread>;//shared_ptr是个类模板

    static int defaultnum=5;
    template<typename T>
    class ThreadPool
    {
        void handletask(std::string& name)//routine里传参就是线程名字
        {
            LOG(LogLevel::INFO)<<"线程"<<name<<",进入handletask的逻辑";
            while(true)
            {
                //1.拿任务
                T t;
                {
                    LockGuard guard(_mutex);
                    while(_taskq.size()==0 &&_isrunning==true)
                    {
                        _wait_num++;
                        _cond.Wait(_mutex);
                        _wait_num--;
                    }
                    //2种情况会走到这，一种是任务队列不是空，另一种是不再运行
                    if(_taskq.size()==0 &&_isrunning==false)
                    {
                        break;//退出任务处理
                    }

                    t=_taskq.front();
                    _taskq.pop();
                }
                //并发去执行任务
                t(name);//线程创建后在routine中调handletask，handletask调任务队列中的方法，handletask和任务加name这个参数就是为了好打日志
            }
            LOG(LogLevel::INFO)<<"线程："<<name<<"退出";



        }
    public:
        ThreadPool(int num = defaultnum)
        :_num(num)
        ,_wait_num(0)
        ,_isrunning(false)
        {
            for(int i=0;i<_num;i++)
            {
                //成员函数地址要  取地址加类域
                _threads.push_back(std::make_shared<Thread>(std::bind(&ThreadPool::handletask,this,std::placeholders::_1)));//线程创建后会进入routine，handletask是构造Thread对象所需要的方法，handletask定为成员函数有this指针，所以要bind来包装handletask，将this直接给个定值
                //最后handletask是要在routine里跑的
                LOG(LogLevel::INFO)<<"构建线程"<<_threads.back()->Name();
            }
        }

        void Equeue(T&& in)
        {
             LockGuard guard(_mutex);
             if(_isrunning==false) return ;

             _taskq.push(std::move(in));//右值会退化成左值，所以要move
             if(_wait_num>0)
                _cond.Notify();
        }

        void Start()
        {
            if(_isrunning==true) return ;//已经运行还start什么
            _isrunning=true;
            for(auto &e:_threads)
            {
                LOG(LogLevel::INFO)<<"启动线程"<<e->Name();
                e->Start();
            }

        }

        void Stop()
        {
            //isrunning也是公共资源，所以也要加锁
            LockGuard guard(_mutex);
            if(_isrunning==true)
            {
                _isrunning=false;//不再加入任务
                //只有handletask走完routine才能走完，routine走完才能join
                //将等待的线程叫醒然后走逻辑去退出handletask
                if(_wait_num>0)
                {
                    _cond.NotifyAll();
                }

            }

        }

        void Wait()
        {
            for(auto& e:_threads)
            {
                e->Join();
                LOG(LogLevel::INFO)<<"回收线程" << e->Name();

            }
        }



    private:
        std::vector<thread_t> _threads;
        int _num;//线程个数
        int _wait_num;//在条件变量下等待的线程个数
        std::queue<T> _taskq;//临界资源

        Mutex _mutex;
        Cond _cond;
        bool _isrunning;//线程池当前是否在跑

    };

}